// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: linchuan

#include "coin2/exchange/huobi/api_order/native_rest_async_asio_subscriber.h"

#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>

#include "coin2/base/log.h"

namespace coin2::exchange::huobi::api_order {

void HuobiRestAsyncAsioSubscriber::QueryAccounts() {
  auto http_context = rest_client()->QueryAccounts();
  if (!http_context) {
    return;
  }
  http_context->handler =
      std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiRestAsyncAsioSubscriber::QueryBalance() {
  if (is_readonly_order_system_ && symcache_->is_margin()) {
    if (!rest_client()->has_margin_crossed_account_id() ||
        !rest_client()->has_margin_isolated_account_id()) {
      QueryAccounts();
      return;
    }
    {
      auto http_context = rest_client()->QueryMarginCrossedAccountBalance();
      http_context->handler =
          std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
      http_driver_->PostAsyncContext(http_context);
    }
    {
      auto http_context = rest_client()->QueryMarginIsolatedAccountBalance();
      http_context->handler =
          std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
      http_driver_->PostAsyncContext(http_context);
    }
    return;
  }
  if (!rest_client()->has_account_id()) {
    QueryAccounts();
    return;
  }
  auto http_context = rest_client()->QueryAccountBalance();
  http_context->handler =
      std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiRestAsyncAsioSubscriber::QueryOpenOrders(const std::string& native_symbol) {
  if (!rest_client()->has_account_id()) {
    QueryAccounts();
    return;
  }

  RestParams params;
  params.set_symbol(native_symbol);
  params.set_size("500");
  auto http_context = rest_client()->QueryOpenOrders(params);
  http_context->handler =
      std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiRestAsyncAsioSubscriber::QueryHistoryOrders(const std::string& native_symbol) {
  if (!rest_client()->has_account_id()) {
    QueryAccounts();
    return;
  }

  RestParams params;
  params.set_symbol(native_symbol);
  params.set_direct("next");
  params.set_size("500");
  auto http_context = rest_client()->QueryHistoryOrders(params);
  http_context->handler =
      std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiRestAsyncAsioSubscriber::QueryFills(const std::string& native_symbol) {
  if (!rest_client()->has_account_id()) {
    QueryAccounts();
    return;
  }

  RestParams params;
  params.set_symbol(native_symbol);
  params.set_direct("next");
  auto http_context = rest_client()->QueryOrderMatchResults(params);
  http_context->handler =
      std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
  http_driver_->PostAsyncContext(http_context);
}

void HuobiRestAsyncAsioSubscriber::QueryTransfer() {
  auto http_context = rest_client()->QueryDeposit();
  if (http_context) {
    http_context->handler =
        std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
    http_driver_->PostAsyncContext(http_context);
  }

  http_context = rest_client()->QueryWithdrawal();
  if (http_context) {
    http_context->handler =
        std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
    http_driver_->PostAsyncContext(http_context);
  }

  if (!rest_client()->has_account_id_type()) {
    QueryAccounts();
    return;
  }

  RestParams params;
  const auto& account_type_map = rest_client()->GetAccountIdTypeMap();
  for (const auto& account : account_type_map) {
    params.set_account_id(account.second);
    http_context = rest_client()->QueryTransfers(params);
    if (!http_context) return;
    http_context->handler =
        std::bind(&HuobiRestAsyncAsioSubscriber::HandleQuery, this, std::placeholders::_1);
    http_driver_->PostAsyncContext(http_context);
  }
}

void HuobiRestAsyncAsioSubscriber::SubscribeAsync() {
  AddPeriodicTask(
      std::bind(&HuobiRestAsyncAsioSubscriber::QueryBalance, this),
      config_.query_account_balance_period_sec(),
      "query_balance");

  if (is_readonly_order_system_ && symcache_->is_spot()) {
    AddPeriodicTask(
        std::bind(&HuobiRestAsyncAsioSubscriber::QueryTransfer, this),
        config_.query_account_balance_period_sec(),
        "query_transfer");
  }

  const auto symbol_list = native_symbols();

  AddPeriodicTask(
      std::bind(&HuobiRestAsyncAsioSubscriber::QueryOpenOrders, this, std::placeholders::_1),
      config_.query_open_orders_period_sec(),
      "query_open_orders",
      symbol_list);

  AddPeriodicTask(
      std::bind(&HuobiRestAsyncAsioSubscriber::QueryHistoryOrders, this, std::placeholders::_1),
      std::bind(
          &HuobiRestAsyncAsioSubscriber::SkipQuery,
          this,
          "query_history_orders",
          std::placeholders::_1),
      config_.query_cancel_orders_period_sec(),
      "query_history_orders",
      symbol_list);

  AddPeriodicTask(
      std::bind(&HuobiRestAsyncAsioSubscriber::QueryFills, this, std::placeholders::_1),
      std::bind(
          &HuobiRestAsyncAsioSubscriber::SkipQuery,
          this,
          "query_fills",
          std::placeholders::_1),
      config_.query_fill_period_sec(),
      "query_fills",
      symbol_list);
}

}  // namespace coin2::exchange::huobi::api_order
